#include <cstdlib>
#include <iostream>
#include "IVEFFunctions.h"

#include "IVEFPlotData.h"

namespace ivef {

// Constructor
PlotData::PlotData()
 :  QObject(),
    m_id( 0 ),
    m_idPresent( false ),
    m_length( 0.0 ),
    m_lengthPresent( false ),
    m_sourceId(),
    m_sourceIdPresent( false ),
    m_updateTime(),
    m_updateTimePresent( false ),
    m_signalStrength( 0 ),
    m_signalStrengthPresent( false ),
    m_width( 0.0 ),
    m_widthPresent( false ),
    m_changed(true)
{
}

// Constructor for parser function
PlotData::PlotData(XmlStreamReader& xml)
 :  QObject(),
    m_id( 0 ),
    m_idPresent( false ),
    m_length( 0.0 ),
    m_lengthPresent( false ),
    m_sourceId(),
    m_sourceIdPresent( false ),
    m_updateTime(),
    m_updateTimePresent( false ),
    m_signalStrength( 0 ),
    m_signalStrengthPresent( false ),
    m_width( 0.0 ),
    m_widthPresent( false ),
    m_changed(true)
{
    QXmlStreamAttributes attr = xml.attributes();
    if ( attr.hasAttribute( "Id" ) )
    {
        if ( !setId( attr.value( "Id" ).toString().toInt() ) )
            xml.validationError( "error set Id = " + attr.value( "Id" ).toString() );
    }
    if ( attr.hasAttribute( "Length" ) )
    {
        if ( !setLength( attr.value( "Length" ).toString().replace(",", ".").toFloat() ) )
            xml.validationError( "error set Length = " + attr.value( "Length" ).toString() );
    }
    if ( attr.hasAttribute( "SourceId" ) )
    {
        if ( !setSourceId( attr.value( "SourceId" ).toString() ) )
            xml.validationError( "error set SourceId = " + attr.value( "SourceId" ).toString() );
    }
    if ( attr.hasAttribute( "UpdateTime" ) )
    {
        if ( !setUpdateTime( QDateTime::fromString(attr.value( "UpdateTime" ).toString(), Qt::ISODate)) )
            xml.validationError( "error set UpdateTime = " + attr.value( "UpdateTime" ).toString() );
    }
    if ( attr.hasAttribute( "SignalStrength" ) )
    {
        if ( !setSignalStrength( attr.value( "SignalStrength" ).toString().toInt() ) )
            xml.validationError( "error set SignalStrength = " + attr.value( "SignalStrength" ).toString() );
    }
    if ( attr.hasAttribute( "Width" ) )
    {
        if ( !setWidth( attr.value( "Width" ).toString().replace(",", ".").toFloat() ) )
            xml.validationError( "error set Width = " + attr.value( "Width" ).toString() );
    }
    bool stop(false);
    while(!xml.atEnd() && !stop)
    {
        QXmlStreamReader::TokenType token = xml.readNext();
        switch ( token )
        {
        case QXmlStreamReader::EndElement:
            if (  xml.name() == "PlotData" )
                stop = true;
            break;
        case QXmlStreamReader::StartElement:
            if ( xml.name() == "Pos" )
            {
                Pos val( xml );
                if ( xml.name() != "Pos" )
                    xml.raiseError( "tag mismatch Pos" );
                else if ( !addPos( val ) )
                    xml.validationError( "error add Pos"  );
            }
            else
            {
                xml.validationError( "unexpected element " + xml.name().toString() );
            }
            break;
        default:
            break;
        }
    }
}

// copy constructor
PlotData::PlotData(const PlotData &val)
 :  QObject(),
    m_poss( val.m_poss ),
    m_id( val.m_id ),
    m_idPresent( val.m_idPresent ),
    m_length( val.m_length ),
    m_lengthPresent( val.m_lengthPresent ),
    m_sourceId( val.m_sourceId ),
    m_sourceIdPresent( val.m_sourceIdPresent ),
    m_updateTime( val.m_updateTime ),
    m_updateTimePresent( val.m_updateTimePresent ),
    m_signalStrength( val.m_signalStrength ),
    m_signalStrengthPresent( val.m_signalStrengthPresent ),
    m_width( val.m_width ),
    m_widthPresent( val.m_widthPresent ),
    m_lastError(),
    m_changed(val.m_changed ),
    m_store(val.m_store )
{
}

// compare
bool PlotData::operator==(const PlotData &val) {

    if (!(m_poss == val.m_poss)) return false;
    if (!(m_idPresent == val.m_idPresent)) return false;
    if (!(m_id == val.m_id)) return false;
    if (!(m_lengthPresent == val.m_lengthPresent)) return false;
    if (!(m_length == val.m_length)) return false;
    if (!(m_sourceIdPresent == val.m_sourceIdPresent)) return false;
    if (!(m_sourceId == val.m_sourceId)) return false;
    if (!(m_updateTimePresent == val.m_updateTimePresent)) return false;
    if (!(m_updateTime == val.m_updateTime)) return false;
    if (!(m_signalStrengthPresent == val.m_signalStrengthPresent)) return false;
    if (!(m_signalStrength == val.m_signalStrength)) return false;
    if (!(m_widthPresent == val.m_widthPresent)) return false;
    if (!(m_width == val.m_width)) return false;
    return true;
}

// assignement
PlotData & PlotData::operator=(const PlotData &val) {

    m_poss = val.m_poss;
    m_idPresent = val.m_idPresent;
    m_id = val.m_id;
    m_lengthPresent = val.m_lengthPresent;
    m_length = val.m_length;
    m_sourceIdPresent = val.m_sourceIdPresent;
    m_sourceId = val.m_sourceId;
    m_updateTimePresent = val.m_updateTimePresent;
    m_updateTime = val.m_updateTime;
    m_signalStrengthPresent = val.m_signalStrengthPresent;
    m_signalStrength = val.m_signalStrength;
    m_widthPresent = val.m_widthPresent;
    m_width = val.m_width;
    m_changed = val.m_changed;
    m_store = val.m_store;
    return *this;
}

// remover for PlotData
bool PlotData::removePos(const Pos& val) {

    if (m_poss.count() <= 0) {
        return false; // scalar already at minOccurs
    }
    m_changed = true;
    return m_poss.removeOne(val);
}

// setter for PlotData
bool PlotData::addPos(const Pos& val) {

    m_poss.append(val);
    m_changed = true;
    return true;
}

// getter for PlotData
const Pos& PlotData::getPosAt(int i) const {

    return m_poss.at(i);
}

// count for PlotData
int PlotData::countOfPoss() const {

    return m_poss.count();
}

// setter for PlotData
bool PlotData::setId(int val) {

    m_idPresent = true;
    m_id = val;
    m_changed = true;
    return true;
}

// getter for PlotData
int PlotData::getId() const {

    return m_id;
}

// setter for PlotData
bool PlotData::setLength(float val) {

    // check if the new value is within min exclusive
    if (val <= 0)
        return false;
    m_lengthPresent = true;
    m_length = val;
    m_changed = true;
    return true;
}

// getter for PlotData
float PlotData::getLength() const {

    return m_length;
}

// check if optional element PlotData has been set
bool PlotData::hasLength() const {

    return m_lengthPresent;
}

// setter for PlotData
bool PlotData::setSourceId(QString val) {

    // check if the new value is within min length
    if (val.length() < 5)
        return false;
    // check if the new value is within max length
    if (val.length() > 15)
        return false;
    m_sourceIdPresent = true;
    m_sourceId = val;
    m_changed = true;
    return true;
}

// getter for PlotData
QString PlotData::getSourceId() const {

    return m_sourceId;
}

// check if optional element PlotData has been set
bool PlotData::hasSourceId() const {

    return m_sourceIdPresent;
}

// setter for PlotData
bool PlotData::setUpdateTime(QDateTime val) {

    m_updateTimePresent = true;
    m_updateTime = val;
    m_changed = true;
    return true;
}

// getter for PlotData
QDateTime PlotData::getUpdateTime() const {

    return m_updateTime;
}

// setter for PlotData
bool PlotData::setSignalStrength(int val) {

    m_signalStrengthPresent = true;
    m_signalStrength = val;
    m_changed = true;
    return true;
}

// getter for PlotData
int PlotData::getSignalStrength() const {

    return m_signalStrength;
}

// check if optional element PlotData has been set
bool PlotData::hasSignalStrength() const {

    return m_signalStrengthPresent;
}

// setter for PlotData
bool PlotData::setWidth(float val) {

    // check if the new value is within min exclusive
    if (val <= 0)
        return false;
    m_widthPresent = true;
    m_width = val;
    m_changed = true;
    return true;
}

// getter for PlotData
float PlotData::getWidth() const {

    return m_width;
}

// check if optional element PlotData has been set
bool PlotData::hasWidth() const {

    return m_widthPresent;
}

// Get XML Representation
const QString& PlotData::toXML(bool outputNamespace) {

    if ( m_changed ) {
        const static QString endAttr( "\"" );
        QString xml = "<PlotData";
        if (outputNamespace)
        {
            xml.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
            xml.append(" xmlns=\"http://www.iala-to-be-confirmed.org/XMLSchema/IVEF/0.2.5\"");
        }
        QString dataMember;
            // check for presence of required  attribute
        if ( m_idPresent) {
            xml.append(" Id=\"" + QString::number( m_id ) + endAttr);
        } else { // required attribute not present
            m_lastError = "Id not set";
            m_store  = QString::null;
            return m_store;
        }
            // check for presence of optional attribute
            if ( hasLength() ) {
                xml.append(" Length=\"" + QString::number( m_length, 'f') + endAttr);
        }
            // check for presence of optional attribute
            if ( hasSourceId() ) {
                xml.append(" SourceId=\"" + encode (m_sourceId) + endAttr);
        }
            // check for presence of required  attribute
        if ( m_updateTimePresent) {
            xml.append(" UpdateTime=\"" + dateToString( m_updateTime) + endAttr);
        } else { // required attribute not present
            m_lastError = "UpdateTime not set";
            m_store  = QString::null;
            return m_store;
        }
            // check for presence of optional attribute
            if ( hasSignalStrength() ) {
                xml.append(" SignalStrength=\"" + QString::number( m_signalStrength ) + endAttr);
        }
            // check for presence of optional attribute
            if ( hasWidth() ) {
                xml.append(" Width=\"" + QString::number( m_width, 'f') + endAttr);
        }
        xml.append(">\n");
        if (m_poss.count() < 0) {
            m_lastError = "not enough Pos values";
            m_store  = QString::null;
            return m_store;
        }
        // add all included data
        for(int i=0; i < m_poss.count(); i++ ) {
            Pos attribute = m_poss.at(i);
            dataMember = attribute.toXML(false);
            if (dataMember != QString::null) {
               xml.append( dataMember );
            } else {
                m_lastError = "Pos:" + attribute.lastError();
                m_store  = QString::null;
                return m_store;
            }
        }
        xml.append( "</PlotData>\n");
        m_store = xml;
        m_changed = false;
    }
    return m_store;
}

// Get String Representation
QString PlotData::toString() const{

    return toString("");
}

// Get String Representation with a lead
QString PlotData::toString(QString lead) const{

    const static QString endAttr( "\n" );
    QString str = lead + "PlotData\n";
    str.append( lead + "    Id = " + QString::number( m_id ) + endAttr);
    // check for presence of optional attribute
    if ( hasLength() ) {
        str.append( lead + "    Length = " + QString::number( m_length, 'f') + endAttr);
    }
    // check for presence of optional attribute
    if ( hasSourceId() ) {
        str.append( lead + "    SourceId = " + m_sourceId + endAttr);
    }
    str.append( lead + "    UpdateTime = " + dateToString( m_updateTime) + endAttr);
    // check for presence of optional attribute
    if ( hasSignalStrength() ) {
        str.append( lead + "    SignalStrength = " + QString::number( m_signalStrength ) + endAttr);
    }
    // check for presence of optional attribute
    if ( hasWidth() ) {
        str.append( lead + "    Width = " + QString::number( m_width, 'f') + endAttr);
    }
    // add all included data
    for(int i=0; i < m_poss.count(); i++ ) {
        Pos attribute = m_poss.at(i);
        str.append( attribute.toString( lead + "    " ) );
    }
    return str;
}

const QString& PlotData::lastError() const {
    return m_lastError;
}

const bool& PlotData::changed() const {
    return m_changed;
}

const QString& PlotData::store() const {
    return m_store;
}


} //end ns
